package com.finger.gulimall.order.service.impl;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.finger.common.constant.MemberConstant;
import com.finger.common.constant.MqOrderConstant;
import com.finger.common.utils.PageUtils;
import com.finger.common.utils.Query;
import com.finger.common.utils.R;
import com.finger.common.vo.MemberVo;
import com.finger.gulimall.order.dao.OrderDao;
import com.finger.gulimall.order.entity.OrderEntity;
import com.finger.gulimall.order.entity.OrderItemEntity;
import com.finger.gulimall.order.entity.to.LockStockTo;
import com.finger.gulimall.order.entity.to.OrderCreateTo;
import com.finger.gulimall.order.entity.to.OrderTo;
import com.finger.gulimall.order.entity.vo.*;
import com.finger.gulimall.order.enume.OrderStatusEnum;
import com.finger.gulimall.order.exception.NotStockException;
import com.finger.gulimall.order.exception.OrderException;
import com.finger.gulimall.order.feign.CartRemoteService;
import com.finger.gulimall.order.feign.MemberRemoteService;
import com.finger.gulimall.order.feign.ProductRemoteService;
import com.finger.gulimall.order.feign.WareRemoteService;
import com.finger.gulimall.order.interceptor.LoginInterceptor;
import com.finger.gulimall.order.service.OrderItemService;
import com.finger.gulimall.order.service.OrderService;
import org.apache.commons.lang.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {

	@Autowired
	private ThreadPoolExecutor executor;

	@Autowired
	private MemberRemoteService memberRemoteService;

	@Autowired
	private CartRemoteService cartRemoteService;

	@Autowired
	private WareRemoteService wareRemoteService;

	@Autowired
	private RedisTemplate<String, String> redisTemplate;

	@Autowired
	private ProductRemoteService productRemoteService;

	@Autowired
	private OrderItemService orderItemService;

	@Autowired
	private RabbitTemplate rabbitTemplate;

	@Override
	public PageUtils queryPage(Map<String, Object> params) {
		IPage<OrderEntity> page = this.page(new Query<OrderEntity>().getPage(params), new QueryWrapper<OrderEntity>());
		return new PageUtils(page);
	}

	/**
	 * 生成订单确认
	 * @return
	 */
	@Override
	public OrderConfirmVo orderConfirmVo() throws Exception {

		OrderConfirmVo confirmVo = new OrderConfirmVo();
		MemberVo memberVo = LoginInterceptor.loginMember.get();
		Long id = memberVo.getId();
		/**
		 * 注：当异步线程去请求远程服务时，异步线程是没有Servlet请求的，所以我们要手动设置异步线程共享当前线程的请求信息
		 */
		RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

		CompletableFuture<Void> addrsFuture = CompletableFuture.runAsync(() -> {

			//异步线程共享当前线程
			RequestContextHolder.setRequestAttributes(requestAttributes);

			// 1.查询收货地址
			R addrsRes = memberRemoteService.findAddrsById(id);
			List<MemberReceiveAddressVo> data = addrsRes.getData(new TypeReference<List<MemberReceiveAddressVo>>() {
			});
			confirmVo.setAddressVos(data);
		}, executor);

		CompletableFuture<Void> itemsFuture = CompletableFuture.runAsync(() -> {

			// 异步线程共享当前线程
			RequestContextHolder.setRequestAttributes(requestAttributes);

			// 2.从购物车中查询全部商品信息
			R checkedItemsRes = cartRemoteService.getItemsForOrderConfrim();
			List<CartItemVo> checkedItems = checkedItemsRes.getData(new TypeReference<List<CartItemVo>>() {
			});
			confirmVo.setCartItemVos(checkedItems);
		}, executor).thenRunAsync(() -> {

			// 3.查询全部库存信息
			List<CartItemVo> itemVos = confirmVo.getCartItemVos();
			if (itemVos.size() > 0) {
				List<Long> skuIds = itemVos.stream().map(item -> item.getSkuId()).collect(Collectors.toList());
				R r = wareRemoteService.getStockForOrderConfirm(skuIds);
				Map<Long, Boolean> hasStock = r.getData(new TypeReference<Map<Long, Boolean>>() {
				});
				System.out.println(hasStock);
				for (CartItemVo itemVo : itemVos) {
					if (hasStock.containsKey(itemVo.getSkuId())) {
						itemVo.setHasStock(hasStock.get(itemVo.getSkuId()));
					}
				}
				confirmVo.setHasStock(hasStock);
			}

		}, executor);

		CompletableFuture.allOf(addrsFuture, itemsFuture).get();


		// 4.查询积分
		confirmVo.setIntegral(memberVo.getIntegration());

		// 5.TODO 防重令牌
		String token = UUID.randomUUID().toString().replace("-", "");
		redisTemplate.opsForValue().set(MemberConstant.MEMBER_ORDER_TOKEN + id, token, 30, TimeUnit.MINUTES);
		confirmVo.setToken(token);

		return confirmVo;
	}

	/**
	 * 计算运费
	 * @param addrId
	 * @return
	 */
	@Override
	public AddrFareVo addrFare(Long addrId) {

		R res = memberRemoteService.addrFare(addrId);

		if (res.getCode() == 0) {
			AddrFareVo data = res.getData(new TypeReference<AddrFareVo>() {
			});
			return data;
		}
		return null;
	}

	/**
	 * 订单提交
	 * 事务解决方案：1.使用seata，强一致性事务
	 * 2.使用rabbitmq，弱一致性事务
	 * @param orderSubmitVo
	 * @return
	 */
	// @GlobalTransactional
	@Transactional
	@Override
	public SubmitOrderRespVo submitOrder(OrderSubmitVo orderSubmitVo) {

		MemberVo memberVo = LoginInterceptor.loginMember.get();
		String token = orderSubmitVo.getToken();
		// 1.Redis验证token，解决接口幂等性问题。0：失败.其他是成功
		Long res = redisTemplate.execute(new DefaultRedisScript<>("if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end", Long.class), Arrays.asList(MemberConstant.MEMBER_ORDER_TOKEN + memberVo.getId()), token);
		SubmitOrderRespVo submitOrderRespVo = new SubmitOrderRespVo();
		submitOrderRespVo.setCode(0);
		// 验证token令牌
		if (res != null && res == 0) {
			// 对比失败
			submitOrderRespVo.setCode(1);
			return submitOrderRespVo;
		} else {
			// 对比成功
			submitOrderRespVo.setCode(0);
			// 2.构建订单和订单项
			OrderCreateTo orderCreateTo = createOrder(orderSubmitVo.getAddrId());

			// 验价
			double differentPrice = orderSubmitVo.getTotalPrice().subtract(orderCreateTo.getOrder().getPayAmount()).doubleValue();

			if (Math.abs(differentPrice) < 0.01) {
				// 前端传过来的订单价格和后台计算的订单价格相减不会超过1毛钱
				// 验价成功
				// 3.保存订单
				saveOrder(orderCreateTo);

				// 4.仓库服务锁定库存
				List<LockStockTo> lockStockTos = orderCreateTo.getOrderItems().stream().map(item -> {
					LockStockTo lockStockTo = new LockStockTo();
					lockStockTo.setSkuId(item.getSkuId());
					lockStockTo.setNum(item.getSkuQuantity());
					return lockStockTo;
				}).collect(Collectors.toList());

				WareSkuLockVo wareSkuLockVo = new WareSkuLockVo();
				wareSkuLockVo.setLockStockTos(lockStockTos);
				wareSkuLockVo.setOrderSn(orderCreateTo.getOrder().getOrderSn());

				R r = wareRemoteService.lockStock(wareSkuLockVo);

				if (r.getCode() != 0) {
					throw new NotStockException("请检查商品是否有库存");
				}

				// 5.TODO:用户服务扣减积分

				OrderEntity order = orderCreateTo.getOrder();
				// 发送延迟消息关单
				OrderTo orderTo = new OrderTo();
				BeanUtils.copyProperties(order, orderTo);
				rabbitTemplate.convertAndSend(MqOrderConstant.ORDER_EVENT_EXCHANGE, MqOrderConstant.ORDER_CREATE_ROUTING_KEY, orderTo);
				try {
					Thread.sleep(3000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				// int i = 10 / 0;
				submitOrderRespVo.setOrder(order);
				return submitOrderRespVo;
			} else {
				// 验价失败
				submitOrderRespVo.setCode(2);
				return submitOrderRespVo;
			}
		}

	}

	/**
	 * 把订单和订单项保存到本地数据库
	 * @param orderCreateTo 订单以及订单项
	 */
	private void saveOrder(OrderCreateTo orderCreateTo) {

		OrderEntity order = orderCreateTo.getOrder();

		List<OrderItemEntity> orderItems = orderCreateTo.getOrderItems();
		// 保存订单
		this.baseMapper.insert(order);

		// 保存订单项
		orderItemService.saveBatch(orderItems);

	}

	/**
	 * 创建订单
	 * @param addrId
	 * @return
	 */
	private OrderCreateTo createOrder(Long addrId) {

		OrderCreateTo orderCreateTo = new OrderCreateTo();
		// 订单号
		String orderSn = UUID.randomUUID().toString().replace("-", "");

		// 1.构建订单
		OrderEntity order = buildOrder(orderSn, addrId);

		// 2.构建订单中项
		List<OrderItemEntity> orderItems = buildOrderItems(orderSn, order.getId());

		// 3.计算价格
		computePrice(order, orderItems);

		orderCreateTo.setOrder(order);
		orderCreateTo.setOrderItems(orderItems);
		orderCreateTo.setFare(order.getFreightAmount());

		orderCreateTo.setPayPrice(order.getTotalAmount().add(order.getFreightAmount()));

		return orderCreateTo;
	}

	/**
	 * 计算价格
	 * @param order
	 * @param orderItems
	 */
	private void computePrice(OrderEntity order, List<OrderItemEntity> orderItems) {

		// 这里简要计算商品总额
		BigDecimal totalPrice = new BigDecimal("0");

		for (OrderItemEntity orderItem : orderItems) {
			totalPrice = totalPrice.add(orderItem.getSkuPrice().multiply(new BigDecimal(orderItem.getSkuQuantity())));
		}
		BigDecimal payAmount = order.getFreightAmount().add(totalPrice);
		// 商品总额，不带运费
		order.setTotalAmount(totalPrice);
		// 应支付金额
		order.setPayAmount(payAmount);

	}

	/**
	 * 构建订单项
	 * @param orderSn
	 * @param
	 * @return
	 */
	private List<OrderItemEntity> buildOrderItems(String orderSn, Long orderId) {

		R checkedItems = cartRemoteService.getItemsForOrderConfrim();
		List<CartItemVo> data = checkedItems.getData(new TypeReference<List<CartItemVo>>() {
		});
		if (data != null && data.size() > 0) {

			// 根据skuId查出对应的spu信息
			List<Long> skuIds = data.stream().map(item -> item.getSkuId()).collect(Collectors.toList());
			R spuRes = productRemoteService.getSpusBySkuIds(skuIds);
			Map<Long, SpuInfoVo> spuResData = spuRes.getData(new TypeReference<Map<Long, SpuInfoVo>>() {
			});

			// 构建商品项
			List<OrderItemEntity> orderItems = data.stream().map(item -> {
				OrderItemEntity orderItem = buildOrderItem(item);
				SpuInfoVo spuInfoVo = spuResData.get(item.getSkuId());

				if (spuInfoVo != null) {
					// 设置spu信息
					orderItem.setSpuId(spuInfoVo.getId());
					orderItem.setCategoryId(spuInfoVo.getBrandId());
					orderItem.setSpuName(spuInfoVo.getSpuName());
					orderItem.setSpuBrand(spuInfoVo.getBrandId().toString());
					orderItem.setSpuPic("default");
					orderItem.setSpuBrand("中国");
					orderItem.setCategoryId(225L);
				}
				orderItem.setOrderId(orderId);
				orderItem.setOrderSn(orderSn);
				return orderItem;
			}).collect(Collectors.toList());

			return orderItems;
		}

		return null;

	}

	/**
	 * 构建某一个商品项
	 * @param item
	 * @return
	 */
	private OrderItemEntity buildOrderItem(CartItemVo item) {

		OrderItemEntity orderItem = new OrderItemEntity();

		orderItem.setSkuAttrsVals(StringUtils.join(item.getSkuAttr(), ";"));
		orderItem.setSkuId(item.getSkuId());
		orderItem.setSkuName(item.getTitle());
		orderItem.setSkuPrice(item.getPrice());
		orderItem.setSkuPic(item.getImage());
		orderItem.setSkuQuantity(item.getCount());
		// 计算当前订单项使用各种优惠卷之后的总金额
		orderItem.setRealAmount(item.getPrice().multiply(new BigDecimal(item.getCount())));
		orderItem.setPromotionAmount(item.getPrice());
		orderItem.setCouponAmount(item.getPrice());
		orderItem.setIntegrationAmount(item.getPrice());
		orderItem.setGiftIntegration(item.getPrice().intValue() % 10);
		orderItem.setGiftGrowth(item.getPrice().intValue() % 10);
		return orderItem;
	}

	/**
	 * 构建订单项
	 * @param orderSn
	 * @param addrId
	 * @return
	 */
	private OrderEntity buildOrder(java.lang.String orderSn, Long addrId) {
		MemberVo memberVo = LoginInterceptor.loginMember.get();
		OrderEntity order = new OrderEntity();
		R r = memberRemoteService.addrFare(addrId);
		AddrFareVo data = r.getData(new TypeReference<AddrFareVo>() {
		});

		long id = IdWorker.getId();
		order.setId(id);
		if (data != null) {
			MemberReceiveAddressVo addr = data.getMemberAddrVo();
			order.setReceiverDetailAddress(addr.getDetailAddress());
			order.setReceiverCity(addr.getCity());
			order.setReceiverName(addr.getName());
			order.setReceiverPhone(addr.getPhone());
			order.setReceiverProvince(addr.getProvince());
			order.setReceiverRegion(addr.getRegion());
			order.setFreightAmount(data.getFare());
		}
		// 设置订单号
		order.setOrderSn(orderSn);

		order.setDeleteStatus(0);
		order.setConfirmStatus(0);
		order.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
		order.setAutoConfirmDay(7);
		order.setMemberId(memberVo.getId());
		order.setMemberUsername(memberVo.getUsername());
		order.setPayType(1);
		order.setSourceType(1);
		order.setCreateTime(new Date());
		order.setModifyTime(new Date());

		return order;
	}

	@Override
	public OrderEntity getOrderByOrderSn(String orderSn) {

		return baseMapper.selectOne(new LambdaQueryWrapper<OrderEntity>().eq(OrderEntity::getOrderSn, orderSn));
	}

	/**
	 * 接受处理延迟队列延迟的消息
	 * @param orderTo
	 */
	@Override
	public void closeOrder(OrderTo orderTo) {

		OrderEntity orderEntity = this.baseMapper.selectById(orderTo.getId());

		if (orderEntity != null && orderEntity.getStatus().equals(OrderStatusEnum.CREATE_NEW.getCode())) {
			// 订单不为空并且订单状态为待支付就关单
			OrderEntity entity = new OrderEntity();
			entity.setId(orderEntity.getId());
			entity.setStatus(OrderStatusEnum.CANCLED.getCode());
			this.baseMapper.updateById(entity);
			// 主动触发解锁库存
			rabbitTemplate.convertAndSend(MqOrderConstant.ORDER_EVENT_EXCHANGE, MqOrderConstant.ORDER_RELEASE_OTHER_ROUTING_KEY, orderTo);
		}
	}

	/**
	 * 获取待支付的订单信息
	 * @param payVo
	 */
	@Override
	public void getPayOrder(PayVo payVo) {

		OrderEntity orderEntity = baseMapper.selectOne(new LambdaQueryWrapper<OrderEntity>().eq(OrderEntity::getOrderSn, payVo.getOut_trade_no()));
		if (orderEntity == null || !OrderStatusEnum.CREATE_NEW.getCode().equals(orderEntity.getStatus())) {
			throw new OrderException("订单不存在或订单状态不为待付款");
		}
		BigDecimal amount = orderEntity.getTotalAmount();
		String totalAmount = amount.setScale(2, BigDecimal.ROUND_UP).toString();
		// 订单总额
		payVo.setTotal_amount(totalAmount);
		// 订单名称
		payVo.setSubject("向马云付款->");
	}

	@Override
	public IPage<OrderEntity> getOrderItems(Map<String, Object> params) {

		IPage<OrderEntity> page = this.page(new Query<OrderEntity>().getPage(params), new QueryWrapper<OrderEntity>().eq("member_id", params.get("memberId")).orderByDesc("create_time"));

		List<OrderEntity> list = page.getRecords();
		List<String> orderSns = list.stream().map(OrderEntity::getOrderSn).collect(Collectors.toList());

		Map<String,List<OrderItemEntity>> map = orderItemService
				.list(new LambdaQueryWrapper<OrderItemEntity>().in(OrderItemEntity::getOrderSn, orderSns))
				.stream().collect(Collectors.groupingBy(OrderItemEntity::getOrderSn));
		for (OrderEntity orderEntity : list) {
			orderEntity.setOrderItems(map.get(orderEntity.getOrderSn()));
		}

		return page;
	}

	/**
	 * 支付成功异步回调更新订单状态
	 * @param orderSn
	 * @param code
	 */
	@Override
	public void updateOrderStatus(String orderSn, Integer code) {
		this.baseMapper.updateOrderStatus(orderSn,code);
	}
}